package org.jpush.core;

import java.util.concurrent.atomic.AtomicInteger;

import org.jpush.core.AbstractChannelStreamer.SendResult;
import org.jpush.packages.OutgoingPacket;
import org.jpush.utils.Pointer;

/**
 * You need to extend this class to provide a way to
 * return a key. The key is unique ID of a {@code LogicalConnection}
 * 
 * @author Lei Wang
 *
 */
public abstract class LogicalConnection
{
    protected PhysicalConnection physicalConnection;

    private long connectTime;
    private AtomicInteger refrenceCounter;

	public LogicalConnection()
	{
		connectTime = System.currentTimeMillis();
	    refrenceCounter = new AtomicInteger(0);
	}

	/**
	 * Push a packet as response
	 * 
	 * @param packet the packet to be pushed
	 * @return The result @see <code>SendResult</code>
	 */
	public SendResult pushPacket(OutgoingPacket packet)
	{
		return physicalConnection.pushPacket(packet);
	}

	/**
	 * Try to push a packet as a response
	 * 
	 * @param packet the packet to be pushed
	 * @return The result @see <code>SendResult</code>
	 */
	public SendResult tryPushPacket(OutgoingPacket packet)
    {
		return physicalConnection.tryPushPacket(packet);
    }
    
	/**
	 * Return the unique ID of this connection
	 * This will be implemented in the sub classes
	 * 
	 * @return the unique ID
	 */
    public abstract String getKey();
    
    /**
     * Get the IP address and port of the connection.
     * 
     * @param pIP   Pointer to store the IP address
     * @param pPort Pointer to store the port
     * @return
     */
    public boolean getChannelInfos(Pointer<String> pIP, Pointer<Integer> pPort)
    {
    	pIP.setValue(physicalConnection.getPeerIP());
        pPort.setValue(new Integer(physicalConnection.getPeerPort()));
        return true;
    }

    /**
     * Return how much time has elapsed since the connection started
     * 
     * @return the time elapsed
     */
    public double getVisitDuration()
    {
        return System.currentTimeMillis() - connectTime;
    }

    /**
     * Drop a reference of this LogicConnection
     */
    public void revert()
    {
    	decrementUsage();
    }
    
    /**
     * Add a reference of this LogicConnection
     */
    void incrementUsage()
    {
    	refrenceCounter.incrementAndGet();
    }
    
    /**
     * Remove a reference of this LogicConnection
     */
    void decrementUsage()
    {
    	refrenceCounter.decrementAndGet();
    }

    /**
     * If this LogicConnection can be deleted, which means
     * it is no longer being used
     * 
     * @return true if it can be deleted
     */
    boolean canDelete()
    {
    	return refrenceCounter.intValue() == 0 && physicalConnection.checkIfUnusedByIOWorkers();
    }
}
